﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using SimpleIR;

namespace PInfoModel.Analyses
{
	public static class GeneralAnalysisMetricsExtractor
	{
		public static GeneralAnalysisMetricsInfo ExtractGeneralInfo(CompleteAnalysisInfoForProgram pip)
		{
			GeneralAnalysisMetricsInfo ginfo = new GeneralAnalysisMetricsInfo();

			foreach(CompleteAnalysisInfoMethod minfo in pip.MethodInfo.Values)
			{
				List<PIModel> inl = new List<PIModel>();
				foreach(CompleteAnalysisInfoMethodContext mcinfo in minfo.AllCallContexts)
					inl.Add(mcinfo.InState);

				if(inl.Count == 0)
					continue;

				ginfo.ProcessCallInList(inl);
			}

			return ginfo;
		}
	}

	/// <summary>
	/// A class that accumulates and manages general info about the heap information we compute:
	/// Min, Max, Total, Average sizes of the heap graphs
	/// </summary>
	public sealed class GeneralAnalysisMetricsInfo
	{
		public int TotalHeapsSeen;
		public int TotalMethodsSeen;
		public Dictionary<int, int> ContextsPerMethodDistribution;

		public int TotalNodesSeen;
		public int MaxNodesSeen;

		public int TotalEdgesSeen;
		public int MaxEdgesSeen;

		public int TotalPureTreeEdges;
		public int TotalCrossEdges;
		public int TotalBackEdges;

		public int TotalInjectiveEdges;
		public int TotalInjectiveOrImmutableEdges;
		public int TotalOwnerEdges;
		public int TotalOwnerOrImmutableEdges;

		public int TotalKnownNonNullEdges;

		public GeneralAnalysisMetricsInfo()
		{
			this.TotalHeapsSeen = 0;
			this.TotalMethodsSeen = 0;
			this.ContextsPerMethodDistribution = new Dictionary<int, int>();

			this.TotalNodesSeen = 0;
			this.MaxNodesSeen = Int32.MinValue;

			this.TotalEdgesSeen = 0;
			this.MaxEdgesSeen = Int32.MinValue;

			this.TotalPureTreeEdges = 0;
			this.TotalCrossEdges = 0;
			this.TotalBackEdges = 0;

			this.TotalInjectiveEdges = 0;
			this.TotalInjectiveOrImmutableEdges = 0;
			this.TotalOwnerEdges = 0;
			this.TotalOwnerOrImmutableEdges = 0;

			this.TotalKnownNonNullEdges = 0;
		}

		public void ProcessCallInList(List<PIModel> methodctxtinmodels)
		{
			this.TotalMethodsSeen++;
			if(!this.ContextsPerMethodDistribution.ContainsKey(methodctxtinmodels.Count))
				this.ContextsPerMethodDistribution.Add(methodctxtinmodels.Count, 0);
			this.ContextsPerMethodDistribution[methodctxtinmodels.Count] = this.ContextsPerMethodDistribution[methodctxtinmodels.Count] + 1;

			foreach(PIModel pim in methodctxtinmodels)
			{
				this.TotalHeapsSeen++;

				this.TotalNodesSeen += pim.Nodes.Count((PIMNode n) => n.HomeLocFor == null);
				this.MaxNodesSeen = Math.Max(pim.Nodes.Count((PIMNode n) => n.HomeLocFor == null), this.MaxNodesSeen);

				int edgect = 0;
				foreach(PIMNode n in pim.Nodes)
				{
					if(n.HomeLocFor == null)
						edgect += n.OutEdges.Count;
				}

				this.TotalEdgesSeen += edgect;
				this.MaxEdgesSeen = Math.Max(edgect, this.MaxEdgesSeen);

				this.CountCrossInjOwnerNull(pim);
			}
		}

		private void CountCrossInjOwnerNull(PIModel m)
		{
			dfstime = 0;
			dfsdiscover = new Dictionary<PIMNode, int>();
			dfsfinish = new Dictionary<PIMNode, int>();
			dfsecc = new Dictionary<PIMEdge, int>();

			foreach(PIMNode n in m.Nodes)
			{
				dfsdiscover.Add(n, -1);
				dfsfinish.Add(n, -1);
			}

			foreach(IRRootIdentifier rrid in m.NameToNodeMap.Keys)
				DFSHelper(m, m.ResolveHomeForRoot(rrid));

			//cout all the styles
			foreach(PIMNode n in m.Nodes)
			{
				if(n.HomeLocFor != null)
					continue;

				foreach(PIMEdge ee in n.OutEdges)
				{
					PIMNode trgt = m.Nodes[ee.TargetAddress];

					//tree,cross,back -- but tree is a but with saturation
					bool isback = trgt.InEdges.Any((PIMEdge ine) => dfsecc.ContainsKey(ine) && dfsecc[ine] == 2);
					bool iscross = (!isback) && trgt.InEdges.Any((PIMEdge ine) => dfsecc.ContainsKey(ine) && dfsecc[ine] == 1);
					bool istree = (!isback) && (!iscross);

					if(isback)
						this.TotalBackEdges++;
					else if(iscross)
						this.TotalCrossEdges++;
					else
						this.TotalPureTreeEdges++;

					if(ee.IsInjective)
						this.TotalInjectiveEdges++;

					if(istree & ee.IsInjective)
						this.TotalOwnerEdges++;

					if(trgt.LocationType.TypeName.Equals("System.String") | ee.IsInjective)
						this.TotalInjectiveOrImmutableEdges++;

					if(trgt.LocationType.TypeName.Equals("System.String") | (istree & ee.IsInjective))
						this.TotalOwnerOrImmutableEdges++;

					if(ee.IsNonNull)
						this.TotalKnownNonNullEdges++;
				}
			}

			dfstime = 0;
			dfsdiscover = null;
			dfsfinish = null;
			dfsecc = null;
		}

		private static int dfstime;
		private static Dictionary<PIMNode, int> dfsdiscover;
		private static Dictionary<PIMNode, int> dfsfinish;
		private static Dictionary<PIMEdge, int> dfsecc; //0 tree, 1 cross, 2 back

		private static void DFSHelper(PIModel m, PIMNode n)
		{
			if(dfsdiscover[n] != -1)
				return;

			dfsdiscover[n] = dfstime++;
			foreach(PIMEdge ee in n.OutEdges)
			{
				PIMNode trgt = m.Nodes[ee.TargetAddress];

				if(dfsdiscover[trgt] == -1)
					dfsecc.Add(ee, 0);
				else
				{
					if(dfsfinish[trgt] == -1)
						dfsecc.Add(ee, 2);
					else
						dfsecc.Add(ee, 1);
				}

				DFSHelper(m, trgt);
			}

			dfsfinish[n] = dfstime++;
		}

		public string EmitString()
		{
			StringBuilder sb = new StringBuilder(AnalyisSupport.InfoHeader + "General Analysis Metrics\n");

			sb.Append(AnalyisSupport.InfoTitle + "Shape Graph Sizes\n");
			sb.Append(String.Format("MaxNodes: {0}\nMaxEdges: {1}\nAvgNodes: {2}\nAvgEdges: {3}\n\n", this.MaxNodesSeen, this.MaxEdgesSeen, ((float)this.TotalNodesSeen) / ((float)this.TotalHeapsSeen), ((float)this.TotalEdgesSeen) / ((float)this.TotalHeapsSeen)));

			if(this.TotalEdgesSeen != 0)
			{
				sb.Append(String.Format("TreeEdge%: {0:p}\nCrossEdge%: {1:p}\nBackEdge%: {2:p}\n\n", ((float)this.TotalPureTreeEdges) / ((float)this.TotalEdgesSeen), ((float)this.TotalCrossEdges) / ((float)this.TotalEdgesSeen), ((float)this.TotalBackEdges) / ((float)this.TotalEdgesSeen)));
				sb.Append(String.Format("Injective%: {0:p}\nInjectiveOrString%: {1:p}\n\n", ((float)this.TotalInjectiveEdges) / ((float)this.TotalEdgesSeen), ((float)this.TotalInjectiveOrImmutableEdges) / ((float)this.TotalEdgesSeen)));
				sb.Append(String.Format("LocalOwner%: {0:p}\nLocalOwnerOrString%: {1:p}\n\n", ((float)this.TotalOwnerEdges) / ((float)this.TotalEdgesSeen), ((float)this.TotalOwnerOrImmutableEdges) / ((float)this.TotalEdgesSeen)));

				sb.Append(String.Format("KnownNonNull%: {0:p}\n\n", ((float)this.TotalKnownNonNullEdges) / ((float)this.TotalEdgesSeen)));
			}

			sb.Append(AnalyisSupport.InfoTitle + "Contexts Per Method Distribution\n");
			sb.Append("#Contexts -> Occourence%\n");
			List<int> ll = new List<int>(this.ContextsPerMethodDistribution.Keys);
			ll.Sort();
			foreach(int cds in ll)
				sb.Append(String.Format("{0} -> {1:p}\n", cds, ((float)this.ContextsPerMethodDistribution[cds]) / ((float)this.TotalMethodsSeen)));
			sb.Append("\n");
		
			return sb.ToString();
		}
	}
}
